package agents;

import java.util.ArrayList;
import java.util.List;
import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.CyclicBehaviour;
import jade.core.behaviours.OneShotBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;


@SuppressWarnings("serial")
public class StrikeAgent extends Agent {
	
	@SuppressWarnings("unused")
	private AID agentId = null;
	
	// Liste aller registrieren Perfectagents
	private List<AID> perfectPlayers = new ArrayList<AID>();
	// Liste aller registrieren Loseragents
	private List<AID> loserPlayers = new ArrayList<AID>();
	// Liste der Spieler die gerade ein Spiel spielen
	private List<AID> currentPlayers = new ArrayList<AID>();
	// Spieler der am Zug ist
	private int currentPlayer = 0;
	private boolean gameStarted = false;
	private boolean gameFinished = true;
	
	// Anzahl der Streichhoelzer
	private int currentSize = 20;
	
	// Strings zum Kommunizieren zwischen den Agenten 
	public final static String REGISTER_PERFECT = "register-perfect";
	public final static String REGISTER_LOSER = "register-loser";
	public final static String DELETE = "delete";
	public final static String RESTART = "restart";
	public static final String YOUR_TURN = "it's your turn baby";
	public static final String REQUEST_STRIKES = "give me the current number of strikes";
	public static final String INFORM_STRIKES = "STRIKES: ";
	public static final String REPLY_STRIKES = "MOVE: ";
	
	// Initialisiert Strikeagent
	@Override
	protected void setup() {
		System.out.println(getLocalName()+" STARTED");
		
		// Registriert Strikeagent beim DF
		this.agentId = new AID(getLocalName(), AID.ISLOCALNAME);

		ServiceDescription sd = new ServiceDescription();
		DFAgentDescription dfd = new DFAgentDescription();

		sd.setType(ManagementAgent.SERVICEID_STRIKE_AGENT);
		sd.setName(getName());

		dfd.setName(getAID());
		dfd.addServices(sd);
		
		try {
			// Register with the Directory Facilitator (DF):
			DFService.register(this, dfd);
			System.out.println(getLocalName()+" registered with DF");
		} catch (FIPAException e) {
			System.err.println(getName() + ": Could not register with DF.");
			doDelete(); // Kill the agent
		}
		
		// Behaviour zum lesen der erhaltenen Nachrichten
		addBehaviour(new CyclicBehaviour(this) {

			@Override
			public void action() {
				// Auslesen der Nachricht
				MessageTemplate mt1 = MessageTemplate.MatchPerformative(ACLMessage.INFORM);
				ACLMessage msg1 = myAgent.receive(mt1);
				if (msg1 != null) {
					// INFORM Message received. Process it
					String msgContent = msg1.getContent();
					
					// Perfectagent registriert sich
					if(msgContent.equals(REGISTER_PERFECT)) {
						// Fuegt neuen Agent zu Liste
						perfectPlayers.add(msg1.getSender());
						System.out.println(getLocalName()+": perfect registered");
						gameStarted = false;
						
					// Loseragent registriert sich
					} else if(msgContent.equals(REGISTER_LOSER)) {
						// Fuegt neuen Agent zu Liste
						loserPlayers.add(msg1.getSender());
						System.out.println(getLocalName()+": loser registered");
						gameStarted = false;
					
					// Spieler hat einen Zug gewahlet
					} else if (msgContent.contains("MOVE")) {
						int strikes = Integer.parseInt(msgContent.substring(6));
						System.out.println(getLocalName()+": current player takes: "+strikes);
						
						// Ueberprueft nach einem gueltigen Zug
						if (strikes > 0 && strikes < 4 && currentSize >= strikes) {
							currentSize = currentSize - strikes;
							System.out.println("new size: "+currentSize);
							
							// Setzt neuen Spieler
							int newPlayer = currentPlayer = (currentPlayer + 1) % 2;
							// Spiel zuende
							if (currentSize == 0) {
								gameFinished = true;
								System.out.println(currentPlayers.get(newPlayer)+ " won");
								// Loescht die Spieler aus der Liste der Spielenden
								currentPlayers.clear();
								currentPlayer = 0;
							} else {
								// Sollte das Spiel nicht vorbei sein ist der naechste Spieler dran
								currentPlayer = newPlayer;
								startGame();
							}
						// Kein gueltiger Zug
						} else {
							// der Spieler der einen Fehler gemacht hat ist nochmal an der Reihe
							startGame();
						}
					
					// Ein Agent soll geloescht werden
					} else if (msgContent.contains(DELETE)) {
						String agentName = msgContent.substring(6);
						System.out.println(msgContent);
						System.out.println(agentName);
						
						// Strikemaster soll geloescht werden
						if (msgContent.contains("strikemaster")) {
							myAgent.doDelete();
							
						// Loseragent soll geloescht werden
						} else if (msgContent.contains("loser")) {
							// Sucht den Agenten der geloescht werden soll
							for (AID aid : loserPlayers) {
								if (aid.toString().contains(agentName)) {
									ACLMessage deleteAgent = new ACLMessage(ACLMessage.INFORM);
									
									deleteAgent.addReceiver(aid);
	
									// Loescht den Agenten
									deleteAgent.setContent(DELETE);
									myAgent.send(deleteAgent);
									loserPlayers.remove(aid);
									return;
								}
							}
							
						// Perfectagent soll geloescht werden
						} else if (msgContent.contains("perfect")) {
							// Sucht den Agenten der geloescht werden soll
							for (AID aid : perfectPlayers) {
								if (aid.toString().contains(agentName)) {
									ACLMessage deleteAgent = new ACLMessage(ACLMessage.INFORM);
									
									deleteAgent.addReceiver(aid);
	
									// Loescht den Agenten
									deleteAgent.setContent(DELETE);
									myAgent.send(deleteAgent);
									perfectPlayers.remove(aid);
									return;
								}
							}
						}
					}
				} else {
					block();
				}
				
				// Liest Nachricht aus
				MessageTemplate mt2 = MessageTemplate.MatchPerformative(ACLMessage.REQUEST);
				ACLMessage msg2 = myAgent.receive(mt2);
				if (msg2 != null) {
					// REQUEST Message received. Process it
					String name = msg2.getContent();
					
					// Der Strikeagent wird nach der Anzahl der Streichhoelzer gefragt
					if(name.equals(REQUEST_STRIKES)) {
						System.out.println(getLocalName()+": strikes requested, current strikes: "+currentSize);
						ACLMessage informStrikes = new ACLMessage(ACLMessage.INFORM);
						
						informStrikes.addReceiver(msg2.getSender());

						// Gibt die Anzahlt der Streichhoelzer zurueck
						informStrikes.setContent(StrikeAgent.INFORM_STRIKES + currentSize);
						myAgent.send(informStrikes);
					}
				} else {
					block();
				}
			}
		});
		
		// Startet das Spiel - Es MUSS! mindestens ein Perfectagent vorhanden sein
		addBehaviour(new CyclicBehaviour(this) {

			@Override
			public void action() {
				// Kein Spiel gestartet und das alte Spiel ist fertig
				if (!gameStarted && gameFinished) {
					// Startet das Spiel
					gameStarted = true;
					
					// Ist mindestens ein Loser und ein Perfect Agent beim Strikemaster registriert
					if ((perfectPlayers.size() > 0 && loserPlayers.size() > 0)) {
						// Beide Spieler spielen jetzt
						currentPlayers.add(perfectPlayers.get(0));
						currentPlayers.add(loserPlayers.get(0));
						
					// Es sind mindestens zwei Perfectagents beim Strikemaster registeriert	
					} else if (perfectPlayers.size() >= 2) {
						// Beide Spieler spielen jetzt
						currentPlayers.add(perfectPlayers.get(0));
						currentPlayers.add(perfectPlayers.get(1));
					} else {
						// Nicht genug moegliche Spieler gefunden 
						// Spiel wird beendet
						gameStarted = false;
					}
					
					// Startet die Kommunikation zwischen den Spielenden Agenten
					if (gameStarted) {
						gameFinished = false;
						
						// Streichhoelzer Anzahl fuer das aktuelle Spiel
						currentSize = 20;
						
						System.out.println(getLocalName()+": game started");
						// Startet das Spiel
						startGame();
					}
				}
			}
		});
    }
        
	@Override
	protected void takeDown() {
		// Vom DF abmelden
		try {
			DFService.deregister(this);
			System.out.println(getLocalName()+" DEREGISTERED WITH THE DF");
			doDelete();
		} catch (FIPAException e) {
			e.printStackTrace();
		}
	}
	
	// Startet Kommunikation mit dem ersten Spieler
	private void startGame() {
		addBehaviour(new OneShotBehaviour(this) {

			@Override
			public void action() {
				ACLMessage yourTurn = new ACLMessage(ACLMessage.INFORM);
				
				yourTurn.addReceiver(currentPlayers.get(currentPlayer));

				// Sagt dem ersten Spieler das er am Zug ist
				yourTurn.setContent(StrikeAgent.YOUR_TURN);
				myAgent.send(yourTurn);
			}	
		});
	}
}